import time
import xbmc, xbmcgui, xbmcaddon #@UnresolvedImport
import threading

__addon__ = xbmcaddon.Addon(id='script.module.alternate.inputs')

ACTION_MOVE_LEFT      = 1
ACTION_MOVE_RIGHT     = 2
ACTION_MOVE_UP        = 3
ACTION_MOVE_DOWN      = 4
ACTION_PAGE_UP        = 5
ACTION_PAGE_DOWN      = 6
ACTION_SELECT_ITEM    = 7
ACTION_HIGHLIGHT_ITEM = 8
ACTION_PARENT_DIR     = 9
ACTION_PREVIOUS_MENU  = 10
ACTION_SHOW_INFO      = 11
ACTION_PAUSE          = 12
ACTION_STOP           = 13
ACTION_NEXT_ITEM      = 14
ACTION_PREV_ITEM      = 15
ACTION_PLAYER_FORWARD = 77
ACTION_PLAYER_REWIND  = 78 
ACTION_SHOW_GUI       = 18
ACTION_PLAYER_PLAY    = 79
ACTION_MOUSE_LEFT_CLICK = 100
ACTION_CONTEXT_MENU   = 117

REMOTE_0 = 58 # remote keys 0-9. are used by multiple windows
REMOTE_1 = 59 # for example in videoFullScreen.xml window id=2005 you can
REMOTE_2 = 60 # enter time (mmss) to jump to particular point in the movie
REMOTE_3 = 61
REMOTE_4 = 62 # with spincontrols you can enter 3digit number to quickly set
REMOTE_5 = 63 # spincontrol to desired value
REMOTE_6 = 64
REMOTE_7 = 65
REMOTE_8 = 66
REMOTE_9 = 67

REMOTE2_0 = 140 # remote keys 0-9. are used by multiple windows
REMOTE2_1 = 141 # for example in videoFullScreen.xml window id=2005 you can
REMOTE2_2 = 142 # enter time (mmss) to jump to particular point in the movie
REMOTE2_3 = 143
REMOTE2_4 = 144 # with spincontrols you can enter 3digit number to quickly set
REMOTE2_5 = 145 # spincontrol to desired value
REMOTE2_6 = 146
REMOTE2_7 = 147
REMOTE2_8 = 148
REMOTE2_9 = 149

#Actually it's show codec info but I'm using in a threaded callback
ACTION_RUN_IN_MAIN = 27

class MTapCh(str):
	def __new__(self,ch,new=None):
		return str.__new__(self, ch)
	
	def __init__(self,ch,new=None):
		self.new = new
	
class MultiTapKeyboard:
	KEYS = (' ',	'.,!?/$:;@#%^&*-+=(){}[]"\'<>\\_~`',
							'abcABC',	'defDEF',
				'ghiGHI',	'jklJKL',	'mnoMNO',
				'pqrsPQRS',	'tuvTUV',	'wxyzWXYZ')
	
	def __init__(self):
		self.isHidden = False
		self.interval = 1
		self.currentNum = -1
		self.currentIdx = 0
		self.lastTime = 0
		self.lastCh = ''
		self.out = ''
		self.cursorIdx = 0
		self._cursor = '[COLOR red]%s|[/COLOR]'
		self.timer = None
	
	def cursor(self,ch=''):
		return self._cursor % ch
	
	def moveCursor(self,offset,update=False):
		if update:
			self.out = self.getResult()
			self.lastCh = ''
			self.currentIdx = 0
			self.currentNum = -1
			self.lastTime = 0
		self.cursorIdx += offset
		if self.cursorIdx < 0:
			self.cursorIdx = 0
		elif self.cursorIdx > len(self.out) + 1:
			self.cursorIdx = len(self.out)
		return self.getOut()[0:self.cursorIdx] + self.cursor() + self.getOut()[self.cursorIdx:]
		
	def getOut(self):
		if self.isHidden: return '*' * len(self.out) 
		return self.out
	
	def getChar(self,num):
		num = int(num)
		now = time.time()
		if self.timer: self.timer.cancel()
		self.timer = threading.Timer(1,self.timeOut)
		self.timer.start()
		new = None
		chars = self.KEYS[num]
		if self.currentNum != num:
			self.currentIdx = 0
			ch = chars[0]
			new = self.lastCh
			self.lastCh = ''
			self.currentIndex = 0
		else:
			if now - self.lastTime > self.interval:
				self.currentIdx = 0
				new = self.lastCh
				self.lastCh = ''
			else:
				self.currentIdx += 1
			if self.currentIdx >= len(chars):
				self.currentIdx = 0
			ch = chars[self.currentIdx]
		self.lastCh = ch
		self.currentNum = num
		self.lastTime = now
		return MTapCh(ch,new)
	
	def timeOut(self):
		xbmc.executebuiltin('Action(codecinfo)')
	
	def numberPressed(self,num):
		ch = self.getChar(num)
		if ch.new:
			self.out = self.out[0:self.cursorIdx] + ch.new + self.out[self.cursorIdx:]
			self.moveCursor(1)
		return self.getOut()[0:self.cursorIdx] + self.cursor(ch) + self.getOut()[self.cursorIdx:]
		
	def backspace(self):
		if self.lastCh:
			self.lastCh = ''
		else:
			self.out = self.out[:self.cursorIdx-1] + self.out[self.cursorIdx:]
			self.moveCursor(-1)
		self.currentIdx = 0
		self.currentNum = -1
		self.lastTime = 0
		return self.getOut()[0:self.cursorIdx] + self.cursor() + self.getOut()[self.cursorIdx:]
	
	def setDefault(self,default,hidden=False):
		self.isHidden = hidden
		self.cursorIdx = len(default)
		self.out = default
		return self.getOut() + self.cursor()
		
	def getResult(self):
		res = self.out[0:self.cursorIdx] + self.lastCh + self.out[self.cursorIdx:]
		if self.lastCh: self.moveCursor(1)
		return res
		
class MultiTapDialog(xbmcgui.WindowXMLDialog):
	def __init__( self, *args, **kwargs ):
		self.default = ''
		self.isHidden = False
		self.out = None
		self.mtap = MultiTapKeyboard()
	
	def onInit(self):
		self.getControl(102).setLabel(self.mtap.setDefault(self.default,self.isHidden))

	def onFocus( self, controlId ):
		pass
	
	def onClick( self, controlID ):
		pass
	
	def onAction(self,action):
		if action == ACTION_RUN_IN_MAIN:
			self.getControl(102).setLabel(self.mtap.moveCursor(0,True))
		elif action == ACTION_PREVIOUS_MENU:
			self.out = None
		elif action == ACTION_MOVE_LEFT:
			self.getControl(102).setLabel(self.mtap.moveCursor(-1,True))
		elif action == ACTION_MOVE_RIGHT:
			self.getControl(102).setLabel(self.mtap.moveCursor(1,True))
		elif action == ACTION_PARENT_DIR:
			self.getControl(102).setLabel(self.mtap.backspace())
		elif action == REMOTE_1 or action == REMOTE2_1:
			self.numberPressed(1)
		elif action == REMOTE_2 or action == REMOTE2_2:
			self.numberPressed(2)
		elif action == REMOTE_3 or action == REMOTE2_3:
			self.numberPressed(3)
		elif action == REMOTE_4 or action == REMOTE2_4:
			self.numberPressed(4)
		elif action == REMOTE_5 or action == REMOTE2_5:
			self.numberPressed(5)
		elif action == REMOTE_6 or action == REMOTE2_6:
			self.numberPressed(6)
		elif action == REMOTE_7 or action == REMOTE2_7:
			self.numberPressed(7)
		elif action == REMOTE_8 or action == REMOTE2_8:
			self.numberPressed(8)
		elif action == REMOTE_9 or action == REMOTE2_9:
			self.numberPressed(9)
		elif action == REMOTE_0 or action == REMOTE2_0:
			self.numberPressed(0)
		elif action == ACTION_SELECT_ITEM:
			self.out = self.mtap.getResult()
			self.close()
		xbmcgui.WindowXMLDialog.onAction(self,action)
		
	def numberPressed(self,num):
		self.getControl(102).setLabel(self.mtap.numberPressed(num))
	
def doMultiTapKeyboard(prompt,default='',hidden=False):
	w = MultiTapDialog("script-alternate-inputs-multitapkeyboard.xml" , __addon__.getAddonInfo('path'), 'Default')
	w.default = default
	w.isHidden = hidden
	w.doModal()
	out = w.out
	del w
	return out


		
		